﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;


namespace DecisionTree
{
    class Methods
    {
        public static double[][] AJ;
        public static double[][] AJTree;
        public static double[][] AJTrain;

        //wczytywanie pliku
        static char[] charSeparators = new char[] { ' ', '\t' };
        public static StreamReader rf;

        public static void loadFile(TextBox tX, TextBox tY)
        {
            StreamReader sr2;
            int y = 0;
            int x = 0;
            string line;

            try
            {
                OpenFileDialog open = new OpenFileDialog();
                open.ShowDialog();
                if (open.FileName != "")
                {
                    StreamReader sr = new StreamReader(open.FileName);

                    while ((line = sr.ReadLine()) != null)
                    {
                        if (y == 0)
                        {
                            string[] words = line.Trim().Split(charSeparators, StringSplitOptions.RemoveEmptyEntries);
                            for (int i = 0; i < words.Length; i++)
                            {
                                x++;
                            }
                        }
                        if (line != "")
                        { y++; }
                    }
                    sr.Close();

                    AJ = new double[x][];//tworzymy tablice z x kolumn.
                    for (int i = 0; i < x; i++)
                    {
                        AJ[i] = new double[y]; //tworzymy tablice z y wierszami
                    }

                    sr2 = new StreamReader(open.FileName);//wczytywanie danych to tablicy AJ
                    y = 0;

                    while ((line = sr2.ReadLine()) != null)
                    {
                        string[] words = line.Trim().Split(charSeparators, StringSplitOptions.RemoveEmptyEntries);
                        x = 0;

                        for (int i = 0; i < words.Length; i++)
                        {
                            AJ[x][y] = double.Parse(words[i].Replace('.', ','));
                            x++;

                        }
                        if (line != "")
                        { y++; }
                    }
                    tX.Text = x.ToString();
                    tY.Text = y.ToString();
                    sr2.Close();

                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("Błąd przy ładowaniu danych z pliku do tworzenia drzewka \n" + ex.ToString());
            }

        }

        public static void loadFileTree()
        {
            StreamReader sr2;
            int y = 0;
            int x = 0;
            string line;

            try
            {
                OpenFileDialog open = new OpenFileDialog();
                open.ShowDialog();
                if (open.FileName != "")
                {
                    StreamReader sr = new StreamReader(open.FileName);

                    while ((line = sr.ReadLine()) != null)
                    {
                        if (y == 1)
                        {
                            string[] words = line.Trim().Split(charSeparators, StringSplitOptions.RemoveEmptyEntries);
                            for (int i = 0; i < words.Length; i++)
                            {
                                x++;
                            }
                        }
                        if (line != "")
                        { y++; }
                    }
                    sr.Close();
                    y -= 1;//zmniejszamy o jeden, bo pierwszy wiersz to nazwy

                    AJTree = new double[x][];//tworzymy tablice z x kolumn.
                    for (int i = 0; i < x; i++)
                    {
                        AJTree[i] = new double[y]; //tworzymy tablice z y wierszami
                    }

                    sr2 = new StreamReader(open.FileName);//wczytywanie danych to tablicy AJTree
                    y = 0;
                    int tmp = 0;
                    while ((line = sr2.ReadLine()) != null)
                    {
                        string[] words = line.Trim().Split(charSeparators, StringSplitOptions.RemoveEmptyEntries);
                        x = 0;
                        if (tmp > 0)
                        {
                            for (int i = 0; i < words.Length; i++)
                            {
                                AJTree[x][y] = double.Parse(words[i]);
                                x++;
                            }

                            if (line != "")
                            { y++; }
                        }
                        tmp++;
                    }
                    sr2.Close();
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("Błąd przy ładowaniu danych z pliku do tworzenia drzewka \n" + ex.ToString());
            }
        }

        public static void loadFileTrain()
        {
            StreamReader sr2;
            int y = 0;
            int x = 0;
            string line;

            try
            {
                OpenFileDialog open = new OpenFileDialog();
                open.ShowDialog();
                if (open.FileName != "")
                {
                    StreamReader sr = new StreamReader(open.FileName);

                    while ((line = sr.ReadLine()) != null)
                    {
                        if (y == 0)
                        {
                            string[] words = line.Trim().Split(charSeparators, StringSplitOptions.RemoveEmptyEntries);
                            for (int i = 0; i < words.Length; i++)
                            {
                                x++;
                            }
                        }
                        if (line != "")
                        { y++; }
                    }
                    sr.Close();

                    AJTrain = new double[x][];//tworzymy tablice z x kolumn.
                    for (int i = 0; i < x; i++)
                    {
                        AJTrain[i] = new double[y]; //tworzymy tablice z y wierszami
                    }

                    sr2 = new StreamReader(open.FileName);//wczytywanie danych to tablicy AJ
                    y = 0;

                    while ((line = sr2.ReadLine()) != null)
                    {
                        string[] words = line.Trim().Split(charSeparators, StringSplitOptions.RemoveEmptyEntries);
                        x = 0;

                        for (int i = 0; i < words.Length; i++)
                        {
                            AJTrain[x][y] = double.Parse(words[i].Replace('.', ','));
                            x++;
                        }
                        if (line != "")
                        { y++; }
                    }
                    sr2.Close();
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("Błąd przy ładowaniu danych z pliku do tworzenia drzewka \n" + ex.ToString());
            }
        }

        public static void saveCriteria(String mnv, String mnvp, String ml, String mvn)
        {
            String fileNameC = "criteria.txt";
            FileStream fsC = new FileStream(fileNameC, FileMode.Create, FileAccess.Write);
            StreamWriter wfC = new StreamWriter(fsC);
            wfC.WriteLine(mnv);
            wfC.WriteLine(mnvp);
            wfC.WriteLine(ml);
            wfC.WriteLine(mvn);
            wfC.Close();
        }

        public static double average(double[] X)
        {
            double a = 0;
            for (int i = 0; i < X.Length; i++)
            {
                a = a + X[i];
            }
            a = a / X.Length;
            return a;
        } //sprawdzone OK

        public static double sum(double[] X)
        {
            double a = 0;
            for (int i = 0; i < X.Length; i++)
            {
                a = a + X[i];
            }
            return a;
        } 

        public static double variance(double[] X, double av)
        {
            double v = 0;
            for (int i = 0; i < X.Length; i++)
            {
                v = v + Math.Pow((X[i] - av), 2);
            }
            v = v / X.Length;
            return v;
        }// sprawdzone OK

        public static void saveFile(String namefile) //zapisywanie zbiorow
        {
            FileStream fs = new FileStream(namefile, FileMode.Append, FileAccess.Write);//Tworzenie plików do zapisywania zbiorow            
            StreamWriter wf = new StreamWriter(fs);

            for (int i = 0; i < AJ[0].Length; i++)
            {
                for (int j = 0; j < AJ.Length; j++)
                {
                    wf.Write(AJ[j][i] + "   ");
                }
                wf.WriteLine();
            } wf.Close();
        }

        public static void readCriteria(TextBox mnv, TextBox mnvp, TextBox ml, TextBox mvn)
        {
            StreamReader rfC = new StreamReader("..\\criteria.txt");//wczytywanie danych to tablicy AJ
            string line;
            List<string> words = new List<string>();
            while ((line = rfC.ReadLine()) != null)
            {
                words.Add(line);
            }
            rfC.Close();
            mnv.Text = words[0];
            mnvp.Text = words[1];
            ml.Text = words[2];
            mvn.Text = words[3];
        }

        public static double[][] readFile(String namefile) //wczytywanie plikow (Nodes)
        {
            String line;
            int x = 0, y = 0;

            rf = new StreamReader(namefile);
            while ((line = rf.ReadLine()) != null)//sprawdzanie ile plik ma  X i Y
            {
                if (y == 0)
                {
                    string[] words = line.Trim().Split(charSeparators, StringSplitOptions.RemoveEmptyEntries);
                    for (int i = 0; i < words.Length; i++)
                    {
                        x++;
                    }
                }
                if (line != "")
                { y++; }
            }
            rf.Close();

            AJ = new double[x][];//tworzymy tablice z x kolumn.
            for (int i = 0; i < x; i++)
            {
                AJ[i] = new double[y]; //tworzymy tablice z y wierszami
            }

            StreamReader rf2 = new StreamReader(namefile);//wczytywanie danych to tablicy AJ
            y = 0;

            while ((line = rf2.ReadLine()) != null)
            {
                string[] words = line.Trim().Split(charSeparators, StringSplitOptions.RemoveEmptyEntries);
                x = 0;

                for (int i = 0; i < words.Length; i++)
                {
                    AJ[x][y] = double.Parse(words[i]);
                    x++;
                }
                if (line != "")
                { y++; }
            }
            rf2.Close();

            return AJ;
        }

        public static bool verificationNode(NodesLR nodeLR, double chMVN, int chMNV, int chMNVP, RichTextBox RT) //sprawdzanie ChL CHR czy spełniaja warunki podziału
        {

            if (nodeLR.NV >= chMNVP)//sprawdzanie czy ilosc wektorow rodzica jest wieksza od minimalnej ilosci podanej
            {
                if (nodeLR.V >= chMVN)//sprawdzanie czy wariancja wezla jest wieksza od min wariancji
                {
                    if (nodeLR.NL >= chMNV && nodeLR.NR >= chMNV) //sprawdzanie czy ilosc wektorow  L i R jest wieksza od min
                    {
                        return true;
                    }
                }
            }
            return false;
        }

        public static void Quicksort(int low, int high, double[] Y) //low index najmnieszy, hight index najwiekszy Y tablica JA[][] mozemy wstawic AJ[] do sortowania
        {
            int i1 = low, j1 = high;
            double pivot = Y[low + (high - low) / 2];

            while (i1 <= j1)
            {
                while (Y[i1] < pivot)
                    i1++;
                while (Y[j1] > pivot)
                    j1--;
                if (i1 <= j1)
                {
                    for (int f = 0; f < AJ.Length; f++)//zamiana wierszy
                    {
                        //if (f != k) //0 - kolumna przez ktora sortujemy nie zmienia miejsca
                        {
                            double tmpD = AJ[f][i1];
                            AJ[f][i1] = AJ[f][j1];
                            AJ[f][j1] = tmpD;
                        }
                    }
                    i1++; j1--;
                }
            }
            if (low < j1)
                Quicksort(low, j1, Y);
            if (i1 < high)
                Quicksort(i1, high, Y);
        }//sprawdzone i dziala na 100% dobrze

        public static NodesLR minVar(double[][] node, RichTextBox RT, int mNVCH)
        {
            int y = (node[0].Length - 1);// -1 bo ostatnia kolumna to Y
            int x = (node.Length - 1);//-1 do QuickSorta X
            double[] TY = node[x];//przypisanie do tablicy wartosci
            double[] TYL;
            double[] TYR;
            int nC;//zapamietuje numer kolumny do kopiowania
            double red1 = 0;
            double red2 = 0;
            double varL;
            double varR;
            int tmp = 0;
            double sumL = 0;
            double sumR = 0;

            NodesLR nodeLR = new NodesLR(); // tworzymy obiekt ktory bedzie zawieral dane wezla L albo R

            for (int i = 0; i < x; i++)//szukanie najlepszej wariancji po kolumnach 
            {
                Quicksort(0, y, node[i]); //ok
                sumL = 0;
                sumR = 0;

                for (int j = mNVCH; j <= (node[0].Length - mNVCH); j++)// szukanie wariancji po wierszach ok, mNVCH - minimalna ilosc wektorow potomka L i R(domyslnie = 1)
                {
                    if (node[i][j - 1] != node[i][j])//sprawdzanie czy nastepny wiersz jest rozny od poprzedniego -1 bo liczymy od 0 ok
                    {
                        varL = 0; varR = 0;//zerowanie variance 

                        TYL = new double[j];//tablica posiadajaca rozmiar j
                        for (int a = 0; a < j; a++)
                        {
                            TYL[a] = TY[a];//przepisanie liczb od 0 do j ;

                        }
                        if (sumL == 0)
                        {
                            sumL = sum(TYL);
                            varL = variance(TYL, sumL / TYL.Length);// varL, sumL / TYL.Length = average
                        }
                        else
                        {
                            sumL = sumL + TYL[j - 1];
                            varL = variance(TYL, sumL / TYL.Length);// varL
                        }
                        

                        nC = TYL.Length;//nC numer tablicy od ktorej ma byc kopiowane (j bo adres j-1 jest w L)
                        TYR = new double[node[0].Length - TYL.Length];//tablica posiadajaca rozmiar (node.lenght-(j))
                        for (int a = 0; a < TYR.Length; a++)
                        {
                            TYR[a] = TY[nC];//przepisywanie liczb od j do konca tablicy
                            nC++;
                        }
                        if (sumR == 0)
                        {
                            sumR = sum(TYR);
                            varR = variance(TYR, sumR / TYR.Length);// varR, sumR / TYR.Length = average
                        }
                        else
                        {
                            sumR = sumR - TYL[j - 1];
                            varR = variance(TYR, sumR / TYR.Length);// varR
                        }

                        red1 = (varL * TYL.Length) + (varR * TYR.Length);//OK liczymy varL i mnozymy razy ilosc wektorow i to samo z warP ok

                        if (tmp == 0)
                        {
                            tmp++; red2 = red1; nodeLR.varL = varL; nodeLR.varR = varR;
                            nodeLR.M = average(node[node.Length - 1]); nodeLR.V = variance(AJ[AJ.Length - 1], average(AJ[node.Length - 1]));
                            nodeLR.NV = node[0].Length; nodeLR.SAN = i + 1; nodeLR.SAV = (node[i][j - 1] + node[i][j]) / 2;
                            nodeLR.ML = average(TYL); nodeLR.MR = average(TYR); nodeLR.NL = TYL.Length; nodeLR.NR = TYR.Length;
                        }

                        if (red1 < red2)
                        {
                            red2 = red1; nodeLR.varL = varL; nodeLR.varR = varR;
                            nodeLR.M = average(node[node.Length - 1]); nodeLR.V = variance(node[node.Length - 1], average(node[node.Length - 1]));
                            nodeLR.NV = node[0].Length; nodeLR.SAN = i + 1; nodeLR.SAV = (node[i][j - 1] + node[i][j]) / 2;
                            nodeLR.ML = average(TYL); nodeLR.MR = average(TYR); nodeLR.NL = TYL.Length; nodeLR.NR = TYR.Length;
                        }
                    }//koniec if sprawdzajacy czy AJ[i][j-1] != AJ[i][j]
                }//koniec forj szukanie po wierszach j-tych 
            }//fori  koniec szukania punktu podzialu
            return nodeLR;
        }

        public static double[][] train()
        {
            int l = AJTrain[0].Length;
            double[][] AJQ = new double[2][];
            AJQ[0] = new double[l];
            AJQ[1] = new double[l];

            int iTree;
            int numA; // początkowy numer atrybutu drzewa z wektora Train

            for (int iTrain = 0; iTrain < AJTrain[0].Length; iTrain++)
            {
                iTree = 0; // zerowanie indeksu
                numA = (int)AJTree[9][iTree] - 1; //początkowy numer atrybutu z wektora Train od ktorego rozpoczynamy sprawdzanie

                for (int i = 0; i < AJTree[0].Length; i++)
                {
                    if (AJTrain[numA][iTrain] > AJTree[10][iTree])//jesli wiekszy to przechodzimy do wezla prawego
                    {
                        iTree = (int)AJTree[5][iTree] - 1; // przypisanie nr wezla do ktorego ma się przeniesc
                        if (AJTree[5][iTree] == 0)
                        {
                            AJQ[0][iTrain] = AJTree[0][iTree]; //zapamietywanie ostatniego wezla
                            AJQ[1][iTrain] = Math.Round(AJTree[6][iTree]); //zapamietywanie wyniku dla rekordu
                            break;
                        }
                        else
                        {
                            numA = (int)AJTree[9][iTree] - 1; // przypisanie nr atrybutu do którego ma przejsc
                        }
                    }
                    else
                    {
                        iTree = (int)AJTree[4][iTree] - 1; // przypisanie do ktorego wezla ma się przeniesc
                        if (AJTree[4][iTree] == 0)
                        {
                            AJQ[0][iTrain] = AJTree[0][iTree];//zapamietywanie ostatniego wezla
                            AJQ[1][iTrain] = Math.Round(AJTree[6][iTree]); //zapamietywanie wyniku dla rekordu
                            break;
                        }
                        else
                        {
                            numA = (int)AJTree[9][iTree] - 1; // przypisanie nr atrybutu do którego ma przejsc
                        }
                    }
                }
            }
            return AJQ;
        }

        public static void saveDataFile(TextBox namefile, double[][] resultVec) //zapisywanie w pliku danych i wyniku z metody train
        {
            FileStream fs = new FileStream(namefile.Text, FileMode.Append, FileAccess.Write);//Tworzenie plików do zapisywania zbiorow            
            StreamWriter wf = new StreamWriter(fs);

            for (int i = 0; i < AJTrain[0].Length; i++)
            {
                for (int j = 0; j < AJTrain.Length; j++)
                {
                    wf.Write(AJTrain[j][i] + "   ");
                }
                wf.Write(resultVec[1][i] + "   ");
                wf.WriteLine();
            } wf.Close();
        }
    }
}
